﻿namespace DCHealthCheck.MvcWeb.Controllers {

    #region Usings
    using DCHealthCheck.Common;
    using DCHealthCheck.DomainModel;
    using DCHealthCheck.MvcWeb.Classes;
    using System;
    using System.Collections.Generic;
    using System.Data.Entity;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using System.Web.Security;
    using System.Web.UI;
    #endregion

    /// <summary>
    /// A Base Controller Class with Error Handling
    /// </summary>
    public class BaseController : Controller {

        #region Members
        /// <summary>
        /// Gets the Controller Name
        /// </summary>
        public string ControllerName { get; protected set; }
        #endregion

        #region Constructor
        public BaseController() {
        }
        #endregion

        #region Methods
        /// <summary>
        /// Clear any Validation Errors
        /// </summary>
        protected void ClearErrors() {
            foreach (var modelStateValue in this.ModelState.Values.Where(ModelState => ModelState.Errors.Count > 0).ToList()) {
                modelStateValue.Errors.Clear();
            }
        }

        protected override void HandleUnknownAction(string actionName) {

            var webErrorInfo = new WebErrorInfo(
                new ApplicationException(),
                this.ControllerName,
                actionName,
                Request.Url.AbsoluteUri

            );

            Session["errorInfo"] = webErrorInfo;
            RedirectToAction("NotFound", "Error").ExecuteResult(this.ControllerContext);

        }

        /// <summary>
        /// Initializes the Controller
        /// </summary>
        /// <param name="requestContext">The request context</param>
        protected override void Initialize(RequestContext requestContext) {
            base.Initialize(requestContext);
        }

        /// <summary>
        /// Controller Action is Executing
        /// </summary>
        /// <param name="filterContext">The request context</param>
        protected override void OnActionExecuting(ActionExecutingContext filterContext) {
            Global.DefaultLog.DebugFormat("[ThreadId: {0} {1}] Url: {2} User: {3}",
                Thread.CurrentThread.ManagedThreadId, Extensions.CurrentMethodName(), Request.Url.AbsoluteUri, string.IsNullOrWhiteSpace(User.Identity.Name) ? "N/A" : User.Identity.Name);

            if (Global.ApplicationUnavailable) {
                if (!string.Equals(this.ControllerName, "Error", StringComparison.OrdinalIgnoreCase)) {
                    RedirectToAction("AppUnavailable", "Error").ExecuteResult(this.ControllerContext);
                }
            }

            string routeName = this.ControllerName + (filterContext.RouteData.Values["ext"] != null ? filterContext.RouteData.Values["ext"].ToString() : string.Empty);
            ViewData.Add("routeName", routeName);

            string sessionInfo = string.Empty;
            if (Request.Cookies[FormsAuthentication.FormsCookieName] != null) {
                try {
                    FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(Request.Cookies[FormsAuthentication.FormsCookieName].Value);
                    sessionInfo = string.Format("Authentication cookie created:<br />{0}<br />Authentication cookie expires:<br />{1}<br />Session Expires:<br />{2}",
                        ticket.IssueDate.ToString("yyyy-MMM-dd HH:mm:ss"),
                        ticket.Expiration.ToString("yyyy-MMM-dd HH:mm:ss"),
                        DateTime.Now.AddMinutes(Session.Timeout).ToString("yyyy-MMM-dd HH:mm:ss"));
                }
                catch { }
            }
            ViewData.Add("SessionInfo", sessionInfo);

            base.OnActionExecuting(filterContext);
        }

        /// <summary>
        /// Handles a unhandled Exception
        /// </summary>
        /// <param name="filterContext">Contains information about the current request and action</param>
        protected override void OnException(ExceptionContext filterContext) {
            Global.DefaultLog.DebugFormat("[ThreadId: {0} {1}] Url: {2} User: {3}",
                Thread.CurrentThread.ManagedThreadId, Extensions.CurrentMethodName(), Request.Url.AbsoluteUri, string.IsNullOrWhiteSpace(User.Identity.Name) ? "N/A" : User.Identity.Name);

            String currentAction = filterContext.RouteData.Values["action"].ToString();
            if (filterContext.Exception is UnauthorizedAccessException) {
                Global.DefaultLog.WarnFormat("[ThreadId: {0}] Url: {1} Access Denied: Controller: {2} Action: {3} User: {4}",
                    Thread.CurrentThread.ManagedThreadId, Request.Url.AbsoluteUri, this.ControllerName, currentAction,
                    (User != null) && (User.Identity != null) && !string.IsNullOrWhiteSpace(User.Identity.Name) ? User.Identity.Name : "N/A");
            }
            else {
                Global.DefaultLog.ErrorFormat("[ThreadId: {0}] Url: {1} Controller: {2} Action: {3} User: {4} {5}",
                    Thread.CurrentThread.ManagedThreadId, Request.Url.AbsoluteUri, this.ControllerName, currentAction,
                    (User != null) && (User.Identity != null) && !string.IsNullOrWhiteSpace(User.Identity.Name) ? User.Identity.Name : "N/A",
                    filterContext.Exception.VerboseExceptionString());
            }

            var pageError = new WebErrorInfo(filterContext.Exception, ControllerName, currentAction);
            filterContext.ExceptionHandled = true;

            var routeValueDictionary = new RouteValueDictionary();
            routeValueDictionary.Add("errorInfo", pageError);
            Session["errorInfo"] = pageError;

            RedirectToAction("Index", "Error", routeValueDictionary).ExecuteResult(this.ControllerContext);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")]
        public string RenderUserControl(string controlName, ControllerContext controllerContext, ViewDataDictionary viewData, TempDataDictionary tempData) {
            Global.DefaultLog.InfoFormat("[ThreadId: {0}] {1} Url: {2} User: {3}", Thread.CurrentThread.ManagedThreadId, Extensions.CurrentMethodName(), Request.Url.AbsoluteUri, string.IsNullOrWhiteSpace(User.Identity.Name) ? "N/A" : User.Identity.Name);

            var viewPage = new System.Web.Mvc.ViewPage();
            viewPage.ViewContext = new System.Web.Mvc.ViewContext(controllerContext, new System.Web.Mvc.WebFormView(controllerContext, "."), viewData, tempData, null);
            Control control = viewPage.LoadControl(controlName);
            viewPage.Controls.Add(control);
            var stringBuilder = new StringBuilder();
            using (var stringWriter = new StringWriter(stringBuilder))
            using (var htmlTextWriter = new HtmlTextWriter(stringWriter)) {
                viewPage.RenderControl(htmlTextWriter);
            }
            return stringBuilder.ToString();
        }

        /// <summary>
        /// Require a Value
        /// </summary>
        /// <param name="Error">Error Message</param>
        /// <param name="Key">Field Name</param>
        protected void Require(string Key, string Error) {
            if (string.IsNullOrWhiteSpace(ModelState[Key].Value.AttemptedValue))
                ModelState.AddModelError(Key, Error);
        }

        /// <summary>
        /// Require an Integer
        /// </summary>
        /// <param name="Friendly">Short name of the Field</param>
        /// <param name="Key">Field Name</param>
        protected void RequireInt(string Friendly, string Key) {
            if (string.IsNullOrWhiteSpace(ModelState[Key].Value.AttemptedValue))
                ModelState.AddModelError(Key, string.Format("{0} is required.", Friendly));

            if (ModelState.IsValid) {
                int attemptedValue = 0;
                if (!int.TryParse(ModelState[Key].Value.AttemptedValue, out attemptedValue))
                    ModelState.AddModelError(Key, string.Format("{0} must be numeric.", Friendly));
            }
        }
        #endregion

    }
}
